

#include <linux/kernel.h>

#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/version.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
 #include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <asm-generic/int-ll64.h>

#include "oled.h"

#define Max_Column  128
#define Max_Row     64

#define WriteCmd(client, cmd) OLED_WriteByte(client, 0x00, cmd)
#define WriteDat(client, data) OLED_WriteByte(client, 0x40, data)

char str[1024];

static struct i2c_client *oled_cli;
static struct i2c_board_info oled_info = {
	I2C_BOARD_INFO("oled", 0x3C),

};
/*临时定义i2c_client,用于通信寻找目的客户端*/
static struct i2c_client *oled_temp;

/*i2c 通信-----写*/
static void OLED_WriteByte(struct i2c_client *client, uint8_t addr, uint8_t data)
{
        uint8_t buf[] = {addr, data};
        i2c_master_send(client, buf, 2);
};
 

 // 清屏函数
static void Oled_Clear(struct i2c_client *client){
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		WriteCmd(client,0xb0+i);
		WriteCmd (client,0x00); 
		WriteCmd (client,0x10); 
		for(n=0;n<128;n++)
			WriteDat(client,0);
	} 
}
//设置光标
void OLED_Set_Pos(unsigned char x,unsigned char y) //x列 y页
{
	WriteCmd(oled_temp,0xb0+y); //设置页 0xB0代表页为第0页
	WriteCmd(oled_temp,(x&0xf0)>>4|0x10); //设置列 高四位 获取高四位数据右移动4位并保存，且让第5位为1
	WriteCmd(oled_temp,(x&0x0f)); //设置列 低四位 保留最后一位数据
}




//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
    unsigned char c=0,i=0;
        c=chr-' ';//得到偏移后的值
        if(x>Max_Column-1){x=0;y=y+2;}
        if(Char_Size ==16)
            {
            OLED_Set_Pos(x,y);
            for(i=0;i<8;i++)
            WriteDat(oled_temp,F8X16[c*16+i]);
            OLED_Set_Pos(x,y+1);
            for(i=0;i<8;i++)
            	WriteDat(oled_temp,F8X16[c*16+i+8]);
            }
            else 
			{
                OLED_Set_Pos(x,y);
                for(i=0;i<6;i++)
                WriteDat(oled_temp,F6x8[c][i]);
 
            }
}


//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
    unsigned char j=0;
    while (chr[j]!='\0')
    {       OLED_ShowChar(x,y,chr[j],Char_Size);
            x+=8;
        if(x>120){x=0;y+=2;}
            j++;
    }

}

/*i2c_driver和device匹配成功的加载回调*/
int OLED_Probe(struct i2c_client *client, const struct i2c_device_id *id)
{
        printk("Thsi is OLED_Probe\n");
        oled_temp = client;
        WriteCmd(client, 0xAE); 
        WriteCmd(client, 0x00); 
        WriteCmd(client, 0x10);
        WriteCmd(client, 0x40); 
        WriteCmd(client, 0x81); 
        WriteCmd(client, 0xcf); 
        WriteCmd(client, 0xa1); 
        WriteCmd(client, 0xc8); 
        WriteCmd(client, 0xa6);
        WriteCmd(client, 0xa8);
        WriteCmd(client, 0x3f); 
        WriteCmd(client, 0xd3); 
        WriteCmd(client, 0x00); 
        WriteCmd(client, 0xd5); 
        WriteCmd(client, 0x80); 
        WriteCmd(client, 0xd9); 
        WriteCmd(client, 0xf1); 
        WriteCmd(client, 0xda); 
        WriteCmd(client, 0x12); 
        WriteCmd(client, 0xdb); 
        WriteCmd(client, 0x40); 
        WriteCmd(client, 0x20); 
        WriteCmd(client, 0x02);
        WriteCmd(client, 0x8d); 
        WriteCmd(client, 0x14); 
        WriteCmd(client, 0xa4); 
        WriteCmd(client, 0xa6); 
        Oled_Clear(client);
        WriteCmd(client, 0xaf); 

        OLED_ShowString(0,0,"OLED is ready!",16);//左右，上下，内容，字号
        return 0;
} 
/*i2c_driver的卸载回调*/
int OLED_Remove(struct i2c_client *client){

	printk("remove!\n");
	return 0;
}
/*用户与内核的交互的写操作*/
static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *fpos)
{
        
	Oled_Clear(oled_temp);

        if (copy_from_user(str, buf, count))
        {
                return -EFAULT;
        }
        *(str+count) = '\0';
        printk("dev_write ,str is %s\n",str);
        OLED_ShowString(0,0,str,16);//左右，上下，内容，字号

        
        return count;
}

/*i2c_device_id结构体,即此结构体的作用为driver驱动会匹配什么名字的设备。*/
static const struct i2c_device_id oled_id[] = {
        {"oled", 0},
        {},
};

/*driver驱动结构体，此作用为定义驱动的名字、开始和退出等操作内容等*/
static struct i2c_driver driver = {
        .driver = {
                /*device和driver的匹配name*/
                .name = "oled",
                .owner = THIS_MODULE,
        },
        .probe = OLED_Probe,
        .remove = OLED_Remove,
        .id_table = oled_id,
};


/*杂项设备的操作集*/
static struct file_operations dev_fops = {
        .owner = THIS_MODULE,
        .write = &dev_write,
};
 /*初始化杂项设备的name和操作集*/
static struct miscdevice mdev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "oled",
        .fops = &dev_fops,
};




static __init int Module_Init(void)
{
	struct i2c_adapter *adap;//先定义一个适配器结构体
	adap = i2c_get_adapter(0);//后面的参数为0，代表是对I2c1进行操作的，此步为取出I2c-0总线
	oled_cli = i2c_new_device(adap,&oled_info);//将刚才定义的i2c适配器结构体放入到总线中，返回的client指针用于对设备卸载时的注销
	i2c_put_adapter(adap);//再把添加完设备的结构体放回,类似互斥锁，同一时刻只有一个进程可以使用
	i2c_add_driver(&driver);//添加I2c的驱动
        misc_register(&mdev);	//申请，注册杂项设备
	return 0;
}
 
static __exit void Module_Exit(void)
{
        /*注销，删除杂项设备文件*/
	misc_deregister(&mdev);
	/*注销，删除客户端*/
	i2c_unregister_device(oled_cli);
        /*卸载驱动*/
	i2c_del_driver(&driver);
}
 
module_init(Module_Init);
module_exit(Module_Exit);
 
MODULE_LICENSE("GPL");